package com.cn.sl.server.common.advice

import cn.dev33.satoken.exception.NotLoginException
import cn.dev33.satoken.exception.NotPermissionException
import com.cn.sl.server.common.constant.MessageConstant
import com.cn.sl.server.common.exception.BusinessException
import com.cn.sl.server.common.exception.EntityNotExistException
import com.cn.sl.server.common.exception.ImportVerifyException
import com.cn.sl.server.common.exception.NoTokenException
import com.cn.sl.server.util.ResponseMessageDto
import com.cn.sl.server.util.badRequest
import com.cn.sl.server.util.extendResponse
import com.cn.sl.server.util.forbidden
import com.cn.sl.server.util.serverError
import com.cn.sl.server.util.unauthorized
import com.fasterxml.jackson.databind.exc.InvalidFormatException
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.dao.DataIntegrityViolationException
import org.springframework.dao.EmptyResultDataAccessException
import org.springframework.http.ResponseEntity
import org.springframework.validation.BindException
import org.springframework.validation.BindingResult
import org.springframework.validation.FieldError
import org.springframework.web.HttpRequestMethodNotSupportedException
import org.springframework.web.bind.MethodArgumentNotValidException
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.RestControllerAdvice
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException
import java.lang.reflect.Method
import java.util.*

/**
 * 通用校验放回校验
 *
 * @author 阮雪峰
 */
@RestControllerAdvice
class ControllerAdvice {
    val log: Logger = LoggerFactory.getLogger(ControllerAdvice::class.java)

    /**
     * form表单验证错误
     *
     * @param e 错误信息
     * @return ResponseEntity<ResponseMessageDto>
    </ResponseMessageDto> */
    @ExceptionHandler(BindException::class)
    fun formBindException(e: BindException): ResponseEntity<ResponseMessageDto> {
        val bindingResult: BindingResult = e.bindingResult
        log.error("表单验证错误, 参数：{}", bindingResult.fieldErrors)
        log.error("程序异常, 详细信息:{}", e.localizedMessage, e)
        return badRequest(Objects.requireNonNull<FieldError>(bindingResult.fieldError).toString())
    }

    /**
     * raw格式json数据校验
     *
     * @param e 错误信息
     * @return ResponseEntity<ResponseMessageDto>
    </ResponseMessageDto> */
    @ExceptionHandler(MethodArgumentNotValidException::class)
    fun rawBindException(e: MethodArgumentNotValidException): ResponseEntity<ResponseMessageDto> {
        log.error("raw格式json数据校验, 参数：{}", e.bindingResult.fieldErrors)
        log.error("程序异常, 详细信息:{}", e.localizedMessage, e)
        val bindingResult: BindingResult = e.bindingResult

        val message = bindingResult.fieldError?.defaultMessage ?: bindingResult.globalError?.defaultMessage
        ?: "提交数据出错了，请联系管理员！"

        return badRequest(message)
    }

    /**
     * 空结果集异常
     *
     * @param e 错误信息
     * @return ResponseEntity<ResponseMessageDto>
    </ResponseMessageDto> */
    @ExceptionHandler(EmptyResultDataAccessException::class)
    fun emptyResultDataAccessException(e: EmptyResultDataAccessException): ResponseEntity<ResponseMessageDto> {
        log.error(e.toString(), e)
        log.error("程序异常, 详细信息:{}", e.localizedMessage, e)
        return badRequest(e.message)
    }

    /**
     * 参数转换异常
     *
     * @param e 错误信息
     * @return ResponseEntity<ResponseMessageDto>
    </ResponseMessageDto> */
    @ExceptionHandler(MethodArgumentTypeMismatchException::class)
    fun methodArgumentTypeMismatchException(e: MethodArgumentTypeMismatchException): ResponseEntity<ResponseMessageDto> {
        val methodName = Objects.requireNonNull<Method>(e.parameter.method).name
        val parameterName: String = e.name
        val parameterType: String = e.parameter.parameterType.simpleName
        val actualParameterType = Objects.requireNonNull<Any>(e.value).javaClass.simpleName
        val actualParameterValue = Objects.requireNonNull<Any>(e.value).toString()
        log.error(
            "方法：{}，参数：{}，类型为：{}，实际类型为：{}，参数值：{}",
            methodName,
            parameterName,
            parameterType,
            actualParameterType,
            actualParameterValue
        )
        log.error("程序异常, 详细信息:{}", e.localizedMessage, e)
        return badRequest(MessageConstant.PARAMETER_TYPE_ERROR)
    }

    /**
     * 参数转换异常
     *
     * @param e 错误信息
     * @return ResponseEntity<ResponseMessageDto>
    </ResponseMessageDto> */
    @ExceptionHandler(InvalidFormatException::class)
    fun invalidFormatException(e: InvalidFormatException): ResponseEntity<ResponseMessageDto> {
        val parameterName = e.pathReference
        val parameterType = e.targetType.simpleName
        val actualParameterType = Objects.requireNonNull(e.value).javaClass.simpleName
        val actualParameterValue = Objects.requireNonNull(e.value).toString()
        log.error(
            "参数：{}，类型为：{}，实际类型为：{}，参数值：{}",
            parameterName,
            parameterType,
            actualParameterType,
            actualParameterValue
        )
        log.error("程序异常, 详细信息:{}", e.localizedMessage, e)
        return badRequest(MessageConstant.PARAMETER_TYPE_ERROR)
    }

    /**
     * 请求没有携带token，提示登录，401
     *
     * @return ResponseEntity<ResponseMessageDto>
    </ResponseMessageDto> */
    @ExceptionHandler(NoTokenException::class)
    fun noTokenException(): ResponseEntity<ResponseMessageDto> {
        log.error("token，提示登录，401")
        return unauthorized(MessageConstant.NO_TOKEN)
    }

    /**
     * token过期或者不是正确的token，412
     *
     * @return ResponseEntity<ResponseMessageDto>
    </ResponseMessageDto> */
    @ExceptionHandler(NotLoginException::class)
    fun tokenException(exception: NotLoginException): ResponseEntity<ResponseMessageDto> {
        exception.printStackTrace()
        log.error("token过期或者不是正确的token，401")
        return unauthorized(MessageConstant.TOKEN_EXPIRED)
    }

    /**
     * 没有权限访问，403
     *
     * @return ResponseEntity<ResponseMessageDto>
    </ResponseMessageDto> */
    @ExceptionHandler(NotPermissionException::class)
    fun noPermissionException(e: NotPermissionException?): ResponseEntity<ResponseMessageDto> {
        log.error("没有权限访问，403", e)
        return forbidden(MessageConstant.NO_PERMISSION_ACCESS)
    }

    /**
     * @param e HttpRequestMethodNotSupportedException
     * @return ResponseEntity<ResponseMessageDto>
    </ResponseMessageDto> */
    @ExceptionHandler(HttpRequestMethodNotSupportedException::class)
    fun httpRequestMethodNotSupported(e: HttpRequestMethodNotSupportedException): ResponseEntity<ResponseMessageDto> {
        log.error("请求方法不支持，支持方法：{}，请求方法：{}", e.supportedHttpMethods, e.method)
        log.error("程序异常, 详细信息:{}", e.localizedMessage, e)
        return serverError(
            String.format(
                "请求方法不支持，支持方法：%s，请求方法：%s",
                e.supportedHttpMethods,
                e.method
            )
        )
    }

    @ExceptionHandler(DataIntegrityViolationException::class)
    fun dataIntegrityViolationException(e: DataIntegrityViolationException): ResponseEntity<ResponseMessageDto> {
        log.error("程序异常, 详细信息:{}", e.localizedMessage, e)
        return serverError("数据不能正确新增或删除，如果是新增操作，请确认数据是否重复新增；如果是删除操作，请确保数据已经没有被使用。")
    }

    @ExceptionHandler(EntityNotExistException::class)
    fun entityNotExistException(e: DataIntegrityViolationException): ResponseEntity<ResponseMessageDto> {
        log.error("程序异常, 详细信息:{}", e.localizedMessage, e)
        return serverError("数据不存在")
    }

    /**
     * 业务异常
     *
     * @return ResponseEntity<ResponseMessageDto>
    </ResponseMessageDto> */
    @ExceptionHandler(BusinessException::class)
    fun businessError(e: BusinessException): ResponseEntity<ResponseMessageDto> {
        log.error("程序异常, 详细信息:{}", e.localizedMessage, e)
        return extendResponse(e.code, e.message)
    }

    /**
     * 业务异常
     *
     * @return ResponseEntity<ResponseMessageDto>
    </ResponseMessageDto> */
    /*@ExceptionHandler(ExcelImportExcelException::class)
    fun excelImportExcelError(e: ExcelImportExcelException, response: HttpServletResponse) {
        log.error("程序异常, 详细信息:{}", e.localizedMessage, e)
        response.outputStream.use { out ->
            response.status = HttpStatus.BAD_REQUEST.value()
            //将生成的excel 写入到流中
            response.contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
            response.setHeader(
                "Content-Disposition",
                "attachment; filename=${URLEncoder.encode(e.filename, StandardCharsets.UTF_8)}"
            )
            response.addHeader("filename", URLEncoder.encode(e.filename, StandardCharsets.UTF_8))
//                        response.contentType = "application/vnd.ms-excel"
//                response.setContentLength(it.size)
            //获取Servlet的输出流ServletOutputStream
            e.workbook.use {
                it.write(out)
            }
        }
    }*/

    /**
     * 服务器异常
     *
     * @return ResponseEntity<ResponseMessageDto>
    </ResponseMessageDto> */
    @ExceptionHandler(Exception::class)
    fun onServerError(e: Exception?): ResponseEntity<ResponseMessageDto> {
        log.error("程序异常, 详细信息:{}", e?.localizedMessage, e)
        return serverError(MessageConstant.SERVER_ERROR)
    }

    /**
     * 导入数据校验异常
     *
     * @return ResponseEntity<ResponseMessageDto>
    </ResponseMessageDto> */
    @ExceptionHandler(ImportVerifyException::class)
    fun importVerifyError(e: ImportVerifyException): ResponseEntity<Map<String, Any>> {
        log.error("业务异常", e)
        return extendResponse(e.code, mapOf(Pair("messages", e.messages)))
    }
}